package club.kynb.mall.product.service.impl;

import club.kynb.mall.product.api.ICouponService;
import club.kynb.mall.product.constant.CouponReceiveWayEnum;
import club.kynb.mall.product.constant.CouponTemplateStatusEnum;
import club.kynb.mall.product.constant.UserCouponStatusEnum;
import club.kynb.mall.product.dto.*;
import club.kynb.mall.product.mysql.mapper.CouponTemplateMapper;
import club.kynb.mall.product.mysql.mapper.UserCouponMapper;
import club.kynb.mall.product.mysql.po.CouponTemplatePO;
import club.kynb.mall.product.mysql.po.UserCouponPO;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.pizza.common.web.exception.Errors;
import org.pizza.common.web.response.CommonCode;
import org.pizza.model.page.PageResult;
import org.pizza.mybatis.plus.support.Pages;
import org.pizza.util.Checker;
import org.pizza.util.Convertor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * @author kynb_club@163.com
 * @since 2021/6/28 10:18 下午
 */
@Slf4j
@Service
@AllArgsConstructor
public class CouponServiceImpl implements ICouponService {
    private final CouponTemplateMapper couponTemplateMapper;
    private final UserCouponMapper userCouponMapper;
    private final TransactionTemplate transactionTemplate;
    private final RedissonClient redissonClient;
    public static final String MALL_RECEIVE_COUPON = "MALL_RECEIVE_COUPON";

    @Override
    public List<CouponTemplateDTO> drawCouponTemplateList() {
        final List<CouponTemplatePO> poList = couponTemplateMapper.selectList(Wrappers.<CouponTemplatePO>lambdaQuery()
                .eq(CouponTemplatePO::getReceiveWay, CouponReceiveWayEnum.ONE.getWay())
                .eq(CouponTemplatePO::getStatus, CouponTemplateStatusEnum.PROCESSING.getStatus())
        );
        return Convertor.convert(poList, CouponTemplateDTO.class);
    }

    @Override
    public List<CouponTemplateDTO> allCouponTemplateList() {
        final List<CouponTemplatePO> poList = couponTemplateMapper.selectList(new QueryWrapper<>());
        return Convertor.convert(poList, CouponTemplateDTO.class);
    }

    @Override
    public List<UserCouponDTO> userCouponList(Long userId, Integer status) {
        final List<UserCouponPO> poList;
        if (status.equals(-1)) {
            poList = userCouponMapper.selectList(Wrappers.<UserCouponPO>lambdaQuery()
                    .eq(UserCouponPO::getUserId, userId)
            );
        } else {
            poList = userCouponMapper.selectList(Wrappers.<UserCouponPO>lambdaQuery()
                    .eq(UserCouponPO::getUserId, userId)
                    .eq(UserCouponPO::getStatus, status)
            );
        }
        return Convertor.convert(poList, UserCouponDTO.class);
    }

    @Override
    public int userCouponNum(Long userId, Integer status) {
        int nums;
        if (status.equals(-1)) {
            nums = userCouponMapper.selectCount(Wrappers.<UserCouponPO>lambdaQuery()
                    .eq(UserCouponPO::getUserId, userId)
            );
        } else {
            nums = userCouponMapper.selectCount(Wrappers.<UserCouponPO>lambdaQuery()
                    .eq(UserCouponPO::getUserId, userId)
                    .eq(UserCouponPO::getStatus, status)
            );
        }
        return nums;
    }

    @Override
    public void receiveCoupon(Long userId, Long couponTemplateId) {
        boolean isLocked = false;
        String key = String.format("%s:%s", MALL_RECEIVE_COUPON, couponTemplateId);
        final RLock lock = redissonClient.getLock(key);
        try {
            Checker.ifNotThrow(isLocked = lock.tryLock(), () -> Errors.BIZ.exception(CommonCode.BUSY));
            final CouponTemplatePO couponTemplatePO = couponTemplateMapper.selectById(couponTemplateId);
            Checker.ifNullThrow(couponTemplatePO, () -> Errors.BIZ.exception("无法领取不存在的优惠券"));
            Checker.ifNotThrow(couponTemplatePO.getReceiveWay().equals(CouponReceiveWayEnum.ONE.getWay()), () -> Errors.BIZ.exception("无法领取系统自动发放的优惠券"));
            Checker.ifNotThrow(couponTemplatePO.getStatus().equals(CouponTemplateStatusEnum.PROCESSING.getStatus()), () -> Errors.BIZ.exception("优惠券未处于可领用状态哦"));
            Checker.ifNotThrow(DateUtil.isIn(new Date(), couponTemplatePO.getReceiveStartTime(), couponTemplatePO.getReceiveEndTime()), () -> Errors.BIZ.exception("不在优惠券的领取时间范围内"));
            Checker.ifNotThrow(couponTemplatePO.getResidueQuantity() > 0, () -> Errors.BIZ.exception("领取优惠券已经领取光了"));
            final Integer count = userCouponMapper.selectCount(Wrappers.<UserCouponPO>lambdaQuery().eq(UserCouponPO::getCouponTemplateId, couponTemplateId).eq(UserCouponPO::getUserId, userId));
            Checker.ifThrow(count > 0, () -> Errors.BIZ.exception("您已经领取这个优惠券了"));
            final UserCouponPO userCouponPO = new UserCouponPO();
            userCouponPO.setCouponTemplateId(couponTemplateId);
            userCouponPO.setUserId(userId);
            userCouponPO.setReceiveTime(new Date());
            userCouponPO.setStatus(UserCouponStatusEnum.UN_USE.getStatus());
            transactionTemplate.executeWithoutResult(transactionStatus -> {
                final int deduct = couponTemplateMapper.deduct(couponTemplateId);
                Checker.ifNotThrow(deduct > 0, () -> Errors.BIZ.exception("领取优惠券失败了，请刷新后重试"));
                final int insert = userCouponMapper.insert(userCouponPO);
                Checker.ifNotThrow(insert > 0, () -> Errors.BIZ.exception("领取优惠券失败了，请刷新后重试"));
            });
        } finally {
            if (isLocked) {
                lock.unlock();
            }
        }
    }

    @Override
    public void useCoupon(Long id) {
        final UserCouponPO userCouponPO = new UserCouponPO();
        userCouponPO.setStatus(UserCouponStatusEnum.USED.getStatus());
        final int update = userCouponMapper.update(userCouponPO, Wrappers.<UserCouponPO>lambdaQuery()
                .eq(UserCouponPO::getId, id)
                .eq(UserCouponPO::getStatus,UserCouponStatusEnum.UN_USE.getStatus())
        );
        Checker.ifNotThrow(update > 0, () -> Errors.BIZ.exception("使用优惠券失败了，请刷新后重试"));
    }

    @Override
    public void backCoupon(Long userId, Long userCouponId) {
        final UserCouponPO userCouponPO = new UserCouponPO();
        userCouponPO.setStatus(UserCouponStatusEnum.UN_USE.getStatus());
        final int update = userCouponMapper.update(userCouponPO, Wrappers.<UserCouponPO>lambdaQuery()
                .eq(UserCouponPO::getId, userCouponId)
                .eq(UserCouponPO::getUserId, userId)
                .eq(UserCouponPO::getStatus,UserCouponStatusEnum.USED.getStatus())
        );
        Checker.ifNotThrow(update > 0, () -> Errors.BIZ.exception("回退优惠券失败了，请重试"));
    }

    @Override
    public PageResult<CouponTemplateDTO> pageCouponTemplate(CouponTemplateParamDTO dto) {
        final LambdaQueryWrapper<CouponTemplatePO> queryWrapper = Wrappers.<CouponTemplatePO>lambdaQuery();
        String keyword = dto.getKeyword();
        if (StringUtils.isNotBlank(keyword)) {
            queryWrapper.like(CouponTemplatePO::getTitle, keyword)
                    .or().like(CouponTemplatePO::getName,keyword);
        }
        IPage<CouponTemplatePO> pageParam = Pages.convert(dto);
        IPage<CouponTemplatePO> page = couponTemplateMapper.selectPage(pageParam, queryWrapper);
        return Pages.convert(page, CouponTemplateDTO.class);
    }

    @Override
    public void createCouponTemplate(CouponTemplateDTO dto) {
        final CouponTemplatePO couponTemplatePO = Convertor.convert(CouponTemplatePO.class, dto);
        final int insert = couponTemplateMapper.insert(couponTemplatePO);
        Checker.ifNotThrow(insert > 0, () -> Errors.SYSTEM.exception("系统异常，添加优惠券失败！"));
    }

    @Override
    public void updateCouponTemplate(CouponTemplateDTO dto) {
        final CouponTemplatePO couponTemplatePO = Convertor.convert(CouponTemplatePO.class, dto);
        final int update = couponTemplateMapper.updateById(couponTemplatePO);
        Checker.ifNotThrow(update > 0, () -> Errors.SYSTEM.exception("系统异常，修改优惠券失败！"));
    }

    @Override
    public void deleteCouponTemplate(Long id) {
        final int delete = couponTemplateMapper.deleteById(id);
        Checker.ifNotThrow(delete > 0, () -> Errors.SYSTEM.exception("系统异常，删除优惠券失败"));
    }

    @Override
    public Optional<CouponTemplateDTO> couponTemplatedetailById(Long id) {
        CouponTemplatePO couponTemplatePO = couponTemplateMapper.selectById(id);
        if (Objects.isNull(couponTemplatePO)) {
            return Optional.empty();
        }
        return Optional.of(Convertor.convert(CouponTemplateDTO.class, couponTemplatePO));
    }

    @Override
    public PageResult<CouponReceiveUserDTO> pageCoupoUser(CouponUserParamDTO dto) {
        final LambdaQueryWrapper<UserCouponPO> queryWrapper = Wrappers.<UserCouponPO>lambdaQuery();
        queryWrapper.eq(UserCouponPO::getCouponTemplateId, dto.getCouponTemplateId());
        Integer status = dto.getStatus();
        if (null != status) {
            queryWrapper.eq(UserCouponPO::getStatus, status);
        }
        IPage<UserCouponPO> pageParam = Pages.convert(dto);
        IPage<UserCouponPO> page = userCouponMapper.selectPage(pageParam, queryWrapper);
        return Pages.convert(page, CouponReceiveUserDTO.class);
    }
}
